package biz

import (
	"encoding/json"
	"errors"
	"time"

	"github.com/gin-gonic/gin"
	gen1 "gitlab1.moolink.net/trade/backend/gen"
	"lab.com/conf"
	"lab.com/consts"
	"lab.com/gen"
	"lab.com/gen/entity"
	"lab.com/request"
)

type paperBiz struct {
}

// Paper  论文信息
var Paper = paperBiz{}

// Add 论文新增
func (paper *paperBiz) Add(c *gin.Context, req request.PaperAddReq) (err error) {
	ctx := conf.Span(c, "biz paper add", nil)

	//解析标签ids
	tagsInfo := make([]int, 0)
	if err := json.Unmarshal([]byte(req.Tags), &tagsInfo); err != nil {
		return err
	}

	if len(tagsInfo) == 0 {
		return errors.New("标签不能为空")
	}
	publicationDate, err := time.Parse("2006-01", req.PublicationDate)
	if err != nil {
		return err
	}
	//开启事务
	if err := conf.LabGen.Transaction(func(tx *gen.Query) error {
		info := &entity.PaperInfo{
			Title:            req.Title,
			PublicationType:  req.PublicationType,
			PublisherFull:    req.PublisherFull,
			PublisherAcronym: req.PublisherAcronym,
			Abstract:         req.Abstract,
			SourceLink:       req.SourceLink,
			CcfRank:          req.CcfRank,
			PublicationDate:  publicationDate,
			Status:           consts.PaperNormal,
		}
		err = tx.PaperInfo.WithContext(ctx).Create(info)
		if err != nil {
			return err
		}
		tagsMap := make([]*entity.PaperTagMap, 0, len(tagsInfo))
		for _, val := range tagsInfo {
			tagsMap = append(tagsMap, &entity.PaperTagMap{
				PaperID: info.ID,
				TagID:   val,
				Status:  consts.PaperNormal,
			})
		}

		err = tx.PaperTagMap.WithContext(ctx).CreateInBatches(tagsMap, len(tagsMap))
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}

	return nil
}

// Edit 论文编辑
func (paper *paperBiz) Edit(c *gin.Context, req request.PaperEditReq) (err error) {
	ctx := conf.Span(c, "biz paper edit", nil)

	publicationDate, err := time.Parse("2006-01", req.PublicationDate)
	if err != nil {
		return err
	}
	if err := conf.LabGen.Transaction(func(tx *gen.Query) error {
		info := &entity.PaperInfo{
			Title:            req.Title,
			PublicationType:  req.PublicationType,
			PublisherFull:    req.PublisherFull,
			PublisherAcronym: req.PublisherAcronym,
			Abstract:         req.Abstract,
			SourceLink:       req.SourceLink,
			CcfRank:          req.CcfRank,
			PublicationDate:  publicationDate,
			Status:           consts.PaperNormal,
		}

		_, err = tx.PaperInfo.WithContext(ctx).
			Where(tx.PaperInfo.ID.Eq(req.ID)).Updates(info)
		if err != nil {
			return err
		}

		//删除原来的论文标签map

		//解析标签ids
		tagsInfo := make([]int, 0)
		if err := json.Unmarshal([]byte(req.Tags), &tagsInfo); err != nil {
			return err
		}

		if len(tagsInfo) == 0 {
			return errors.New("标签不能为空")
		}
		mapInfo := &entity.PaperTagMap{
			Status: consts.PaperDelete,
		}
		_, err = tx.PaperTagMap.WithContext(ctx).Select(tx.PaperTagMap.Status).Where(tx.PaperTagMap.PaperID.Eq(req.ID)).Updates(mapInfo)
		if err != nil {
			return err
		}

		//新增当前论文标签map
		tagsMap := make([]*entity.PaperTagMap, 0, len(tagsInfo))
		for _, val := range tagsInfo {
			tagsMap = append(tagsMap, &entity.PaperTagMap{
				PaperID: req.ID,
				TagID:   val,
				Status:  consts.PaperNormal,
			})
		}

		err = tx.PaperTagMap.WithContext(ctx).CreateInBatches(tagsMap, len(tagsMap))
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}
	return nil

}

// Delete 论文删除
func (paper *paperBiz) Delete(c *gin.Context, req request.PaperDeleteReq) (err error) {
	ctx := conf.Span(c, "biz paper delete", nil)

	if err := conf.LabGen.Transaction(func(tx *gen.Query) error {
		info := &entity.PaperInfo{
			Status: consts.PaperDelete,
		}
		_, err = tx.PaperInfo.WithContext(ctx).Where(tx.PaperInfo.ID.Eq(req.ID)).Updates(info)
		if err != nil {
			return err
		}
		mapInfo := &entity.PaperTagMap{
			Status: consts.PaperDelete,
		}
		_, err = tx.PaperTagMap.WithContext(ctx).Where(tx.PaperTagMap.PaperID.Eq(req.ID)).Updates(mapInfo)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}

// List 论文列表
func (paper *paperBiz) List(c *gin.Context, req request.PaperListReq) (out request.PaperListRes, err error) {
	ctx := conf.Span(c, "biz paper list", nil)
	paperModel := conf.LabGen.PaperInfo
	paperTagMapModel := conf.LabGen.PaperTagMap
	paperTagModel := conf.LabGen.PaperTag

	where := []gen1.Condition{paperModel.Status.Eq(consts.PaperNormal), paperTagMapModel.Status.Eq(consts.PaperNormal)}
	if req.Title != "" {
		where = append(where, paperModel.Title.Like("%"+req.Title+"%"))
	}
	if req.PublicationType != 0 {
		where = append(where, paperModel.PublicationType.Eq(req.PublicationType))
	}
	if req.PublicationDateStart != "" {
		if t, err := time.Parse("2006-01", req.PublicationDateStart); err != nil {
			return out, err
		} else {
			where = append(where, paperModel.PublicationDate.Gte(t))
		}
	}
	if req.PublicationDateEnd != "" {
		if t, err := time.Parse("2006-01", req.PublicationDateEnd); err != nil {
			return out, err
		} else {
			where = append(where, paperModel.PublicationDate.Lte(t))
		}
	}
	if req.CcfRank != 0 {
		where = append(where, paperModel.CcfRank.Eq(req.CcfRank))
	}
	if req.PublisherAcronym != "" {
		where = append(where, paperModel.PublisherAcronym.Like("%"+req.PublisherAcronym+"%"))
	}
	//解析标签ids
	if req.Tags != "" {
		tagsInfo := make([]int, 0)
		if err := json.Unmarshal([]byte(req.Tags), &tagsInfo); err != nil {
			return out, err
		}

		if len(tagsInfo) != 0 {
			where = append(where, paperTagMapModel.TagID.In(tagsInfo...))
		}
	}

	info, count, err := paperModel.WithContext(ctx).Where(where...).Group(paperModel.ID).Order(paperModel.ID.Desc()).LeftJoin(paperTagMapModel, paperTagMapModel.PaperID.EqCol(paperModel.ID)).FindByPage(req.Offset, req.Size)
	if err != nil {
		return out, err
	}
	paperIDs := make([]int, 0)
	for _, v := range info {
		paperIDs = append(paperIDs, v.ID)
	}
	mapWhere := []gen1.Condition{paperTagMapModel.Status.Eq(consts.PaperNormal), paperTagMapModel.PaperID.In(paperIDs...)}
	mapInfos, err := paperTagMapModel.WithContext(ctx).Where(mapWhere...).Find()
	if err != nil {
		return out, err
	}
	paperTagMapInfo := make(map[int][]int)
	tagIDs := make([]int, 0)
	for _, mapInfo := range mapInfos {
		tagIDs = append(tagIDs, mapInfo.TagID)
		paperTagMapInfo[mapInfo.PaperID] = append(paperTagMapInfo[mapInfo.PaperID], mapInfo.TagID)
	}

	tagNameMap := make(map[int]entity.PaperTag)
	tagNameInfos, err := paperTagModel.WithContext(ctx).Where([]gen1.Condition{paperTagModel.ID.In(tagIDs...), paperTagModel.Status.Eq(consts.PaperNormal)}...).Find()
	if err != nil {
		return out, err
	}
	for _, tagName := range tagNameInfos {
		tagNameMap[tagName.ID] = *tagName
	}
	list := make([]request.PaperBasicInfo, 0, len(info))
	for _, val := range info {
		var tags []request.TagBasicInfo
		for _, tag := range paperTagMapInfo[val.ID] {
			if tagNameMap[tag].Status == consts.PaperNormal {
				tags = append(tags, request.TagBasicInfo{
					ID:      tag,
					TagName: tagNameMap[tag].TagName,
				})
			}

		}
		list = append(list, request.PaperBasicInfo{
			ID:                val.ID,
			Title:             val.Title,
			PublicationType:   consts.PublicationTypeDesc[val.PublicationType],
			PublicationTypeID: val.PublicationType,
			PublisherFull:     val.PublisherFull,
			PublisherAcronym:  val.PublisherAcronym,
			Abstract:          val.Abstract,
			SourceLink:        val.SourceLink,
			PublicationDate:   val.PublicationDate.Format("2006-01"),
			CcfRank:           consts.PaperCcfRankDesc[val.CcfRank],
			CcfRankID:         val.CcfRank,
			Tags:              tags,
		})
	}
	out.List = list
	out.Count = int(count)
	return out, nil
}

// Detail 论文详情
func (paper *paperBiz) Detail(c *gin.Context, req request.PaperDetailReq) (out request.PaperDetailRes, err error) {
	ctx := conf.Span(c, "biz paper detail", nil)
	paperModel := conf.LabGen.PaperInfo
	paperTagMapModel := conf.LabGen.PaperTagMap
	paperTagModel := conf.LabGen.PaperTag

	info, err := paperModel.WithContext(ctx).Where(paperModel.ID.Eq(req.ID)).First()
	if err != nil {
		return out, err
	}
	out.ID = info.ID
	out.Title = info.Title
	out.PublicationType = consts.PublicationTypeDesc[info.PublicationType]
	out.PublicationTypeID = info.PublicationType
	out.PublisherFull = info.PublisherFull
	out.PublisherAcronym = info.PublisherAcronym
	out.SourceLink = info.SourceLink
	out.CcfRank = consts.PaperCcfRankDesc[info.CcfRank]
	out.CcfRankID = info.CcfRank
	out.PublicationDate = info.PublicationDate.Format("2006-01")
	out.Abstract = info.Abstract

	mapWhere := []gen1.Condition{paperTagMapModel.Status.Eq(consts.PaperNormal), paperTagMapModel.PaperID.Eq(req.ID), paperTagModel.Status.Eq(consts.PaperNormal)}
	tagInfos, err := paperTagModel.WithContext(ctx).Where(mapWhere...).LeftJoin(paperTagMapModel, paperTagMapModel.TagID.EqCol(paperTagModel.ID)).Group(paperTagModel.ID).Find()
	if err != nil {
		return out, err
	}

	for _, tagInfo := range tagInfos {
		out.Tags = append(out.Tags, request.TagBasicInfo{
			ID:      tagInfo.ID,
			TagName: tagInfo.TagName,
		})
	}
	return out, nil
}

// TagAdd 标签新增
func (paper *paperBiz) TagAdd(c *gin.Context, req request.TagAddReq) (err error) {
	ctx := conf.Span(c, "biz paper tag add", nil)
	paperTagModel := conf.LabGen.PaperTag
	info := &entity.PaperTag{
		TagName: req.TagName,
		Status:  consts.PaperNormal,
	}
	err = paperTagModel.WithContext(ctx).Create(info)
	if err != nil {
		return err
	}
	return nil
}

// TagEdit 标签编辑
func (paper *paperBiz) TagEdit(c *gin.Context, req request.TagEditReq) (err error) {
	ctx := conf.Span(c, "biz paper tag edit", nil)
	paperTagModel := conf.LabGen.PaperTag
	info := &entity.PaperTag{
		TagName: req.TagName,
	}
	_, err = paperTagModel.WithContext(ctx).Select(paperTagModel.TagName).Where(paperTagModel.ID.Eq(req.ID)).Updates(info)
	if err != nil {
		return err
	}
	return nil
}

// TagDelete 标签删除
func (paper *paperBiz) TagDelete(c *gin.Context, req request.TagDeleteReq) (err error) {
	ctx := conf.Span(c, "biz paper tag delete", nil)
	paperTagModel := conf.LabGen.PaperTag
	info := &entity.PaperTag{
		Status: consts.PaperDelete,
	}
	_, err = paperTagModel.WithContext(ctx).Select(paperTagModel.Status).Where(paperTagModel.ID.Eq(req.ID)).Updates(info)
	if err != nil {
		return err
	}
	return nil
}

// TagList 标签列表
func (paper *paperBiz) TagList(c *gin.Context, req request.TagListReq) (out request.TagListRes, err error) {
	ctx := conf.Span(c, "biz paper tag list", nil)
	paperTagModel := conf.LabGen.PaperTag
	where := []gen1.Condition{paperTagModel.Status.Eq(consts.PaperNormal)}
	if req.TagName != "" {
		where = append(where, paperTagModel.TagName.Like("%"+req.TagName+"%"))
	}
	info, count, err := paperTagModel.WithContext(ctx).Where(where...).Order(paperTagModel.ID.Desc()).FindByPage(req.Offset, req.Size)
	if err != nil {
		return out, err
	}
	list := make([]request.TagBasicInfo, 0, len(info))
	for _, val := range info {
		list = append(list, request.TagBasicInfo{
			ID:      val.ID,
			TagName: val.TagName,
		})
	}
	out.List = list
	out.Count = int(count)
	return out, nil
}

// TagOption 标签选项
func (paper *paperBiz) TagOption(c *gin.Context) (out request.TagListRes, err error) {
	ctx := conf.Span(c, "biz paper tag option", nil)
	paperTagModel := conf.LabGen.PaperTag
	where := []gen1.Condition{paperTagModel.Status.Eq(consts.PaperNormal)}
	info, err := paperTagModel.WithContext(ctx).Where(where...).Order(paperTagModel.ID.Desc()).Find()
	if err != nil {
		return out, err
	}
	list := make([]request.TagBasicInfo, 0, len(info))
	for _, val := range info {
		list = append(list, request.TagBasicInfo{
			ID:      val.ID,
			TagName: val.TagName,
		})
	}
	out.List = list
	out.Count = len(info)
	return out, nil
}
